package com.zhaojun.cloud.common.util;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * 提供zip文件的解压缩接口： 输入：zipFileName（zip文件的绝对路径），outputDirectory（zip文件解压缩后的存放路径） 输出： 说明：初始化函数 unZipFile： 输入：无 输出： 说明：解压缩zip文件，解压缩后存放到outputDirectory路径下 GetZipFileList： 输入：无 输出：list
 * 说明：解压缩后的文件列表绝对路径存放在ArrayList动态数组中
 */
public abstract class ZipUtil {

	private static transient Logger logger = LoggerFactory.getLogger(ZipUtil.class);

	public static final int BUFFER = 1024;// 缓存大小

	public static final String DEFAULT_ENCODING = "GBK";

	/**
	 * zip压缩功能. 压缩baseDir(文件夹目录)下所有文件，包括子目录
	 * 
	 * @throws Exception
	 */
	public static void zipFile(String baseDir, String zipFileName) throws Exception {
		zipFile(baseDir, zipFileName, DEFAULT_ENCODING);
	}

	public static void zipFile(String baseDir, String zipFileName, String encoding) throws Exception {
		List<File> fileList = getSubFiles(new File(baseDir));
		ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFileName));
		zos.setEncoding(encoding);
		ZipEntry ze = null;
		byte[] buf = new byte[BUFFER];
		int readLen = 0;
		for (int i = 0; i < fileList.size(); i++) {
			File f = fileList.get(i);
			ze = new ZipEntry(getAbsFileName(baseDir, f));
			ze.setSize(f.length());
			ze.setTime(f.lastModified());
			zos.putNextEntry(ze);
			InputStream is = new BufferedInputStream(new FileInputStream(f));
			while ((readLen = is.read(buf, 0, BUFFER)) != -1) {
				zos.write(buf, 0, readLen);
			}
			is.close();
		}
		zos.close();
	}

	public static void zipFile(File originalFile, String zipFileName) throws Exception {
		zipFile(originalFile, zipFileName, DEFAULT_ENCODING);
	}

	public static void zipFile(File originalFile, String zipFileName, String encoding) throws Exception {
		ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFileName));
		zos.setEncoding(encoding);
		ZipEntry ze = null;
		byte[] buf = new byte[BUFFER];
		int readLen = 0;
		ze = new ZipEntry(originalFile.getName());
		ze.setSize(originalFile.length());
		ze.setTime(originalFile.lastModified());
		zos.putNextEntry(ze);
		InputStream is = new BufferedInputStream(new FileInputStream(originalFile));
		while ((readLen = is.read(buf, 0, BUFFER)) != -1) {
			zos.write(buf, 0, readLen);
		}
		is.close();
		zos.close();
	}

	public static byte[] zipFile(byte[] bytes, String originalFileName, String zipFileName) throws Exception {
		return zipFile(bytes, originalFileName, zipFileName, DEFAULT_ENCODING);
	}

	public static byte[] zipFile(byte[] bytes, String originalFileName, String zipFileName, String encoding) throws Exception {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ZipOutputStream zos = new ZipOutputStream(baos);
		zos.setEncoding(encoding);
		ZipEntry ze = null;
		byte[] buf = new byte[BUFFER];
		int readLen = 0;
		ze = new ZipEntry(originalFileName);
		ze.setSize(bytes.length);
		ze.setTime(System.currentTimeMillis());
		zos.putNextEntry(ze);
		InputStream is = new BufferedInputStream(new ByteArrayInputStream(bytes));
		while ((readLen = is.read(buf, 0, BUFFER)) != -1) {
			zos.write(buf, 0, readLen);
		}
		is.close();
		zos.close();

		return baos.toByteArray();
	}

	public static byte[] zipFile(byte[][] bytesArray, List<String> originalFileNames, String zipFileName) throws Exception {
		return zipFile(bytesArray, originalFileNames, zipFileName, DEFAULT_ENCODING);
	}

	public static byte[] zipFile(byte[][] bytesArray, List<String> originalFileNames, String zipFileName, String encoding) throws Exception {
		if (null == bytesArray) {
			throw new IllegalArgumentException("要压缩的字节流为空");
		}
		if (bytesArray.length != originalFileNames.size()) {
			throw new IllegalArgumentException("要压缩的字节数组数量和文件名数量不一致");
		}

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ZipOutputStream zos = new ZipOutputStream(baos);
		zos.setEncoding(encoding);
		ZipEntry ze = null;
		byte[] buf = new byte[BUFFER];
		int readLen = 0;
		for (int i = 0; i < bytesArray.length; i++) {
			byte[] bytes = bytesArray[i];
			String fileName = originalFileNames.get(i);
			ze = new ZipEntry(fileName);
			ze.setSize(bytes.length);
			ze.setTime(System.currentTimeMillis());
			zos.putNextEntry(ze);
			InputStream is = new BufferedInputStream(new ByteArrayInputStream(bytes));
			while ((readLen = is.read(buf, 0, BUFFER)) != -1) {
				zos.write(buf, 0, readLen);
			}
			is.close();
		}
		zos.close();

		return baos.toByteArray();
	}

	/**
	 * 给定根目录，返回另一个文件名的相对路径，用于zip文件中的路径.
	 * 
	 * @param baseDir
	 *            java.lang.String 根目录
	 * @param realFileName
	 *            java.io.File 实际的文件名
	 * @return 相对文件名
	 */
	private static String getAbsFileName(String baseDir, File realFileName) {
		File real = realFileName;
		File base = new File(baseDir);
		String ret = real.getName();
		while (true) {
			real = real.getParentFile();
			if (real == null)
				break;
			if (real.equals(base))
				break;
			else
				ret = real.getName() + "/" + ret;
		}
		return ret;
	}

	/**
	 * 取得指定目录下的所有文件列表，包括子目录.
	 * 
	 * @param baseDir
	 *            File 指定的目录
	 * @return 包含java.io.File的List
	 */
	private static List<File> getSubFiles(File baseDir) {
		List<File> ret = new ArrayList<File>();
		File[] tmp = baseDir.listFiles();
		if (null != tmp) {
			for (int i = 0; i < tmp.length; i++) {
				if (tmp[i].isFile())
					ret.add(tmp[i]);
				if (tmp[i].isDirectory())
					ret.addAll(getSubFiles(tmp[i]));
			}
		}
		return ret;
	}

	/**
	 * 解压缩文件 如果outputDierctory为null，解压到zipFileName文件的当前目录下
	 * 
	 * @param zipFileName
	 *            zip文件的绝对路径
	 * @param outputDirectory
	 *            zip文件解压缩后的存放路径
	 * @return 解压后的目录名
	 * @throws IOException
	 */
	public static String unZip(String zipFileName, String outputDirectory) throws IOException {
		return unZip(zipFileName, outputDirectory, DEFAULT_ENCODING);
	}

	public static String unZip(String zipFileName, String outputDirectory, String encoding) throws IOException {
		if (zipFileName == null) {
			return "";
		}

		if (outputDirectory == null) {
			outputDirectory = zipFileName.substring(0, zipFileName.replace('\\', '/').lastIndexOf("/"));
		}

		File file = new File(zipFileName);
		if (!file.exists()) {
			return "";
		}
		createDirectory(outputDirectory, "");

		ZipFile zipFile = null;
		InputStream in = null;
		FileOutputStream out = null;
		try {
			zipFile = new ZipFile(zipFileName, encoding);
			Enumeration<?> entries = zipFile.getEntries();
			ZipEntry zipEntry = null;
			while (entries.hasMoreElements()) {
				zipEntry = (ZipEntry) entries.nextElement();
				if (zipEntry.isDirectory()) {
					String name = zipEntry.getName();
					name = name.substring(0, name.length() - 1);
					File f = new File(outputDirectory + File.separator + name);
					f.mkdir();
				} else {
					String fileName = zipEntry.getName();
					fileName = fileName.replace('\\', '/');
					// 文件在多层目录下 这时需要创建目录如：d1/d2/1.txt
					if (fileName.indexOf("/") != -1) {
						createDirectory(outputDirectory, fileName.substring(0, fileName.lastIndexOf("/")));
						fileName = fileName.substring(fileName.lastIndexOf("/") + 1, fileName.length());
					}

					File outFile = new File(outputDirectory + File.separator + zipEntry.getName());

					outFile.createNewFile();
					try {
						in = zipFile.getInputStream(zipEntry);
						out = new FileOutputStream(outFile);

						byte[] by = new byte[4096];
						int c;
						while ((c = in.read(by)) != -1) {
							out.write(by, 0, c);
						}
					} finally {
						if (null != in) {
							try {
								in.close();
							} catch (IOException e) {
								logger.error("error", e);
							} finally {
								in = null;
							}
						}
						if (null != out) {
							try {
								out.close();
							} catch (IOException e) {
								logger.error("error", e);
							} finally {
								out = null;
							}
						}
					}
				}
			}
		} finally {
			if (null != zipFile) {
				try {
					zipFile.close();
				} catch (IOException e) {
					logger.error("error", e);
				} finally {
					zipFile = null;
				}
			}
		}
		return outputDirectory;
	}

	// 创建目录
	private static void createDirectory(String directory, String subDirectory) {
		String dir[];
		File fl = new File(directory);
		try {
			if (subDirectory == "" && fl.exists() != true)
				fl.mkdirs();
			else if (subDirectory != "") {
				dir = subDirectory.replace("\\", "/").split("/");
				for (int i = 0; i < dir.length; i++) {
					File subFile = new File(directory + File.separator + dir[i]);
					if (!subFile.exists())
						subFile.mkdir();
					directory += File.separator + dir[i];
				}
			}
		} catch (Exception e) {
			logger.error("error", e);
		}
	}

}
